/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of the copyright holders may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

#include "precomp.hpp"

namespace cv {

template<typename T> static inline Scalar rawToScalar(const T& v) {
    Scalar s;
    typedef typename DataType<T>::channel_type T1;
    int i, n = DataType<T>::channels;
    for (i = 0; i < n; i++) {
        s.val[i] = ((T1*)&v)[i];
    }
    return s;
}

/****************************************************************************************\
*                                        sum                                             *
\****************************************************************************************/

template<typename T, typename WT, typename ST, int BLOCK_SIZE>
static Scalar sumBlock_(const Mat& srcmat) {
    assert(DataType<T>::type == srcmat.type());
    Size size = getContinuousSize(srcmat);
    ST s0 = 0;
    WT s = 0;
    int y, remaining = BLOCK_SIZE;

    for (y = 0; y < size.height; y++) {
        const T* src = (const T*)(srcmat.data + srcmat.step * y);
        int x = 0;
        while (x < size.width) {
            int limit = std::min(remaining, size.width - x);
            remaining -= limit;
            limit += x;
            for (; x <= limit - 4; x += 4) {
                s += src[x];
                s += src[x + 1];
                s += src[x + 2];
                s += src[x + 3];
            }
            for (; x < limit; x++) {
                s += src[x];
            }
            if (remaining == 0 || (x == size.width && y == size.height - 1)) {
                s0 += s;
                s = 0;
                remaining = BLOCK_SIZE;
            }
        }
    }
    return rawToScalar(s0);
}

template<typename T, typename ST>
static Scalar sum_(const Mat& srcmat) {
    assert(DataType<T>::type == srcmat.type());
    Size size = getContinuousSize(srcmat);
    ST s = 0;

    for (int y = 0; y < size.height; y++) {
        const T* src = (const T*)(srcmat.data + srcmat.step * y);
        int x = 0;
        for (; x <= size.width - 4; x += 4) {
            s += src[x];
            s += src[x + 1];
            s += src[x + 2];
            s += src[x + 3];
        }
        for (; x < size.width; x++) {
            s += src[x];
        }
    }
    return rawToScalar(s);
}

typedef Scalar(*SumFunc)(const Mat& src);

Scalar sum(const Mat& m) {
    static SumFunc tab[] = {
        sumBlock_ < uchar, unsigned, double, 1 << 24 > ,
        sumBlock_ < schar, int, double, 1 << 24 > ,
        sumBlock_ < ushort, unsigned, double, 1 << 16 > ,
        sumBlock_ < short, int, double, 1 << 16 > ,
        sum_<int, double>,
        sum_<float, double>,
        sum_<double, double>, 0,

        sumBlock_ < Vec<uchar, 2>, Vec<unsigned, 2>, Vec<double, 2>, 1 << 24 > ,
        sumBlock_ < Vec<schar, 2>, Vec<int, 2>, Vec<double, 2>, 1 << 24 > ,
        sumBlock_ < Vec<ushort, 2>, Vec<unsigned, 2>, Vec<double, 2>, 1 << 16 > ,
        sumBlock_ < Vec<short, 2>, Vec<int, 2>, Vec<double, 2>, 1 << 16 > ,
        sum_<Vec<int, 2>, Vec<double, 2> >,
        sum_<Vec<float, 2>, Vec<double, 2> >,
        sum_<Vec<double, 2>, Vec<double, 2> >, 0,

        sumBlock_ < Vec<uchar, 3>, Vec<unsigned, 3>, Vec<double, 3>, 1 << 24 > ,
        sumBlock_ < Vec<schar, 3>, Vec<int, 3>, Vec<double, 3>, 1 << 24 > ,
        sumBlock_ < Vec<ushort, 3>, Vec<unsigned, 3>, Vec<double, 3>, 1 << 16 > ,
        sumBlock_ < Vec<short, 3>, Vec<int, 3>, Vec<double, 3>, 1 << 16 > ,
        sum_<Vec<int, 3>, Vec<double, 3> >,
        sum_<Vec<float, 3>, Vec<double, 3> >,
        sum_<Vec<double, 3>, Vec<double, 3> >, 0,

        sumBlock_ < Vec<uchar, 4>, Vec<unsigned, 4>, Vec<double, 4>, 1 << 24 > ,
        sumBlock_ < Vec<schar, 4>, Vec<int, 4>, Vec<double, 4>, 1 << 24 > ,
        sumBlock_ < Vec<ushort, 4>, Vec<unsigned, 4>, Vec<double, 4>, 1 << 16 > ,
        sumBlock_ < Vec<short, 4>, Vec<int, 4>, Vec<double, 4>, 1 << 16 > ,
        sum_<Vec<int, 4>, Vec<double, 4> >,
        sum_<Vec<float, 4>, Vec<double, 4> >,
        sum_<Vec<double, 4>, Vec<double, 4> >, 0
    };

    Size size = m.size();
    SumFunc func;

    CV_Assert(m.channels() <= 4);

    func = tab[m.type()];
    CV_Assert(func != 0);

    return func(m);
}

/****************************************************************************************\
*                                     countNonZero                                       *
\****************************************************************************************/

template<typename T>
static int countNonZero_(const Mat& srcmat) {
    //assert( DataType<T>::type == srcmat.type() );
    const T* src = (const T*)srcmat.data;
    size_t step = srcmat.step / sizeof(src[0]);
    Size size = getContinuousSize(srcmat);
    int nz = 0;

    for (; size.height--; src += step) {
        int x = 0;
        for (; x <= size.width - 4; x += 4) {
            nz += (src[x] != 0) + (src[x + 1] != 0) + (src[x + 2] != 0) + (src[x + 3] != 0);
        }
        for (; x < size.width; x++) {
            nz += src[x] != 0;
        }
    }
    return nz;
}

typedef int (*CountNonZeroFunc)(const Mat& src);

int countNonZero(const Mat& m) {
    static CountNonZeroFunc tab[] = {
        countNonZero_<uchar>, countNonZero_<uchar>, countNonZero_<ushort>,
        countNonZero_<ushort>, countNonZero_<int>, countNonZero_<float>,
        countNonZero_<double>, 0
    };

    CountNonZeroFunc func = tab[m.depth()];
    CV_Assert(m.channels() == 1 && func != 0);
    return func(m);
}


/****************************************************************************************\
*                                         mean                                           *
\****************************************************************************************/

template<typename T, typename WT, typename ST, int BLOCK_SIZE>
static Scalar meanBlock_(const Mat& srcmat, const Mat& maskmat) {
    assert(DataType<T>::type == srcmat.type() &&
           CV_8U == maskmat.type() && srcmat.size() == maskmat.size());
    Size size = getContinuousSize(srcmat, maskmat);
    ST s0 = 0;
    WT s = 0;
    int y, remaining = BLOCK_SIZE, pix = 0;

    for (y = 0; y < size.height; y++) {
        const T* src = (const T*)(srcmat.data + srcmat.step * y);
        const uchar* mask = maskmat.data + maskmat.step * y;
        int x = 0;
        while (x < size.width) {
            int limit = std::min(remaining, size.width - x);
            remaining -= limit;
            limit += x;
            for (; x < limit; x++)
                if (mask[x]) {
                    s += src[x], pix++;
                }
            if (remaining == 0 || (x == size.width && y == size.height - 1)) {
                s0 += s;
                s = 0;
                remaining = BLOCK_SIZE;
            }
        }
    }
    return rawToScalar(s0) * (1. / std::max(pix, 1));
}


template<typename T, typename ST>
static Scalar mean_(const Mat& srcmat, const Mat& maskmat) {
    assert(DataType<T>::type == srcmat.type() &&
           CV_8U == maskmat.type() && srcmat.size() == maskmat.size());
    Size size = getContinuousSize(srcmat, maskmat);
    ST s = 0;
    int y, pix = 0;

    for (y = 0; y < size.height; y++) {
        const T* src = (const T*)(srcmat.data + srcmat.step * y);
        const uchar* mask = maskmat.data + maskmat.step * y;
        for (int x = 0; x < size.width; x++)
            if (mask[x]) {
                s += src[x], pix++;
            }
    }
    return rawToScalar(s) * (1. / std::max(pix, 1));
}

typedef Scalar(*MeanMaskFunc)(const Mat& src, const Mat& mask);

Scalar mean(const Mat& m) {
    return sum(m) * (1. / std::max(m.rows * m.cols, 1));
}

Scalar mean(const Mat& m, const Mat& mask) {
    static MeanMaskFunc tab[] = {
        meanBlock_ < uchar, unsigned, double, 1 << 24 > , 0,
        meanBlock_ < ushort, unsigned, double, 1 << 16 > ,
        meanBlock_ < short, int, double, 1 << 16 > ,
        mean_<int, double>,
        mean_<float, double>,
        mean_<double, double>, 0,

        meanBlock_ < Vec<uchar, 2>, Vec<unsigned, 2>, Vec<double, 2>, 1 << 24 > , 0,
        meanBlock_ < Vec<ushort, 2>, Vec<unsigned, 2>, Vec<double, 2>, 1 << 16 > ,
        meanBlock_ < Vec<short, 2>, Vec<int, 2>, Vec<double, 2>, 1 << 16 > ,
        mean_<Vec<int, 2>, Vec<double, 2> >,
        mean_<Vec<float, 2>, Vec<double, 2> >,
        mean_<Vec<double, 2>, Vec<double, 2> >, 0,

        meanBlock_ < Vec<uchar, 3>, Vec<unsigned, 3>, Vec<double, 3>, 1 << 24 > , 0,
        meanBlock_ < Vec<ushort, 3>, Vec<unsigned, 3>, Vec<double, 3>, 1 << 16 > ,
        meanBlock_ < Vec<short, 3>, Vec<int, 3>, Vec<double, 3>, 1 << 16 > ,
        mean_<Vec<int, 3>, Vec<double, 3> >,
        mean_<Vec<float, 3>, Vec<double, 3> >,
        mean_<Vec<double, 3>, Vec<double, 3> >, 0,

        meanBlock_ < Vec<uchar, 4>, Vec<unsigned, 4>, Vec<double, 4>, 1 << 24 > , 0,
        meanBlock_ < Vec<ushort, 4>, Vec<unsigned, 4>, Vec<double, 4>, 1 << 16 > ,
        meanBlock_ < Vec<short, 4>, Vec<int, 4>, Vec<double, 4>, 1 << 16 > ,
        mean_<Vec<int, 4>, Vec<double, 4> >,
        mean_<Vec<float, 4>, Vec<double, 4> >,
        mean_<Vec<double, 4>, Vec<double, 4> >, 0
    };

    if (!mask.data) {
        return mean(m);
    }

    CV_Assert(m.channels() <= 4 && m.size() == mask.size() && mask.type() == CV_8U);

    MeanMaskFunc func = tab[m.type()];
    CV_Assert(func != 0);

    return func(m, mask);
}

/****************************************************************************************\
*                                       meanStdDev                                       *
\****************************************************************************************/

template<typename T, typename SqT> struct SqrC1 {
    typedef T type1;
    typedef SqT rtype;
    rtype operator()(type1 x) const { return (SqT)x * x; }
};

template<typename T, typename SqT> struct SqrC2 {
    typedef Vec<T, 2> type1;
    typedef Vec<SqT, 2> rtype;
    rtype operator()(const type1& x) const { return rtype((SqT)x[0] * x[0], (SqT)x[1] * x[1]); }
};

template<typename T, typename SqT> struct SqrC3 {
    typedef Vec<T, 3> type1;
    typedef Vec<SqT, 3> rtype;
    rtype operator()(const type1& x) const
    { return rtype((SqT)x[0] * x[0], (SqT)x[1] * x[1], (SqT)x[2] * x[2]); }
};

template<typename T, typename SqT> struct SqrC4 {
    typedef Vec<T, 4> type1;
    typedef Vec<SqT, 4> rtype;
    rtype operator()(const type1& x) const
    { return rtype((SqT)x[0] * x[0], (SqT)x[1] * x[1], (SqT)x[2] * x[2], (SqT)x[3] * x[3]); }
};

template<> inline double SqrC1<uchar, double>::operator()(uchar x) const
{ return CV_SQR_8U(x); }

template<> inline Vec<double, 2> SqrC2<uchar, double>::operator()(const Vec<uchar, 2>& x) const
{ return Vec<double, 2>(CV_SQR_8U(x[0]), CV_SQR_8U(x[1])); }

template<> inline Vec<double, 3> SqrC3<uchar, double>::operator()(const Vec<uchar, 3>& x) const
{ return Vec<double, 3>(CV_SQR_8U(x[0]), CV_SQR_8U(x[1]), CV_SQR_8U(x[2])); }

template<> inline Vec<double, 4> SqrC4<uchar, double>::operator()(const Vec<uchar, 4>& x) const
{ return Vec<double, 4>(CV_SQR_8U(x[0]), CV_SQR_8U(x[1]), CV_SQR_8U(x[2]), CV_SQR_8U(x[3])); }


template<class SqrOp> static void
meanStdDev_(const Mat& srcmat, Scalar& _mean, Scalar& _stddev) {
    SqrOp sqr;
    typedef typename SqrOp::type1 T;
    typedef typename SqrOp::rtype ST;
    typedef typename DataType<ST>::channel_type ST1;

    assert(DataType<T>::type == srcmat.type());
    Size size = getContinuousSize(srcmat);
    ST s = 0, sq = 0;

    for (int y = 0; y < size.height; y++) {
        const T* src = (const T*)(srcmat.data + srcmat.step * y);
        for (int x = 0; x < size.width; x++) {
            T v = src[x];
            s += v;
            sq += sqr(v);
        }
    }

    _mean = _stddev = Scalar();
    double scale = 1. / std::max(size.width * size.height, 1);
    for (int i = 0; i < DataType<ST>::channels; i++) {
        double t = ((ST1*)&s)[i] * scale;
        _mean.val[i] = t;
        _stddev.val[i] = std::sqrt(std::max(((ST1*)&sq)[i] * scale - t * t, 0.));
    }
}

template<class SqrOp> static void
meanStdDevMask_(const Mat& srcmat, const Mat& maskmat,
                Scalar& _mean, Scalar& _stddev) {
    SqrOp sqr;
    typedef typename SqrOp::type1 T;
    typedef typename SqrOp::rtype ST;
    typedef typename DataType<ST>::channel_type ST1;

    assert(DataType<T>::type == srcmat.type() &&
           CV_8U == maskmat.type() &&
           srcmat.size() == maskmat.size());
    Size size = getContinuousSize(srcmat, maskmat);
    ST s = 0, sq = 0;
    int pix = 0;

    for (int y = 0; y < size.height; y++) {
        const T* src = (const T*)(srcmat.data + srcmat.step * y);
        const uchar* mask = maskmat.data + maskmat.step * y;
        for (int x = 0; x < size.width; x++)
            if (mask[x]) {
                T v = src[x];
                s += v;
                sq += sqr(v);
                pix++;
            }
    }
    _mean = _stddev = Scalar();
    double scale = 1. / std::max(pix, 1);
    for (int i = 0; i < DataType<ST>::channels; i++) {
        double t = ((ST1*)&s)[i] * scale;
        _mean.val[i] = t;
        _stddev.val[i] = std::sqrt(std::max(((ST1*)&sq)[i] * scale - t * t, 0.));
    }
}

typedef void (*MeanStdDevFunc)(const Mat& src, Scalar& mean, Scalar& stddev);

typedef void (*MeanStdDevMaskFunc)(const Mat& src, const Mat& mask,
                                   Scalar& mean, Scalar& stddev);

void meanStdDev(const Mat& m, Scalar& mean, Scalar& stddev, const Mat& mask) {
    static MeanStdDevFunc tab[] = {
        meanStdDev_<SqrC1<uchar, double> >, 0,
        meanStdDev_<SqrC1<ushort, double> >,
        meanStdDev_<SqrC1<short, double> >,
        meanStdDev_<SqrC1<int, double> >,
        meanStdDev_<SqrC1<float, double> >,
        meanStdDev_<SqrC1<double, double> >, 0,

        meanStdDev_<SqrC2<uchar, double> >, 0,
        meanStdDev_<SqrC2<ushort, double> >,
        meanStdDev_<SqrC2<short, double> >,
        meanStdDev_<SqrC2<int, double> >,
        meanStdDev_<SqrC2<float, double> >,
        meanStdDev_<SqrC2<double, double> >, 0,

        meanStdDev_<SqrC3<uchar, double> >, 0,
        meanStdDev_<SqrC3<ushort, double> >,
        meanStdDev_<SqrC3<short, double> >,
        meanStdDev_<SqrC3<int, double> >,
        meanStdDev_<SqrC3<float, double> >,
        meanStdDev_<SqrC3<double, double> >, 0,

        meanStdDev_<SqrC4<uchar, double> >, 0,
        meanStdDev_<SqrC4<ushort, double> >,
        meanStdDev_<SqrC4<short, double> >,
        meanStdDev_<SqrC4<int, double> >,
        meanStdDev_<SqrC4<float, double> >,
        meanStdDev_<SqrC4<double, double> >, 0
    };

    static MeanStdDevMaskFunc mtab[] = {
        meanStdDevMask_<SqrC1<uchar, double> >, 0,
        meanStdDevMask_<SqrC1<ushort, double> >,
        meanStdDevMask_<SqrC1<short, double> >,
        meanStdDevMask_<SqrC1<int, double> >,
        meanStdDevMask_<SqrC1<float, double> >,
        meanStdDevMask_<SqrC1<double, double> >, 0,

        meanStdDevMask_<SqrC2<uchar, double> >, 0,
        meanStdDevMask_<SqrC2<ushort, double> >,
        meanStdDevMask_<SqrC2<short, double> >,
        meanStdDevMask_<SqrC2<int, double> >,
        meanStdDevMask_<SqrC2<float, double> >,
        meanStdDevMask_<SqrC2<double, double> >, 0,

        meanStdDevMask_<SqrC3<uchar, double> >, 0,
        meanStdDevMask_<SqrC3<ushort, double> >,
        meanStdDevMask_<SqrC3<short, double> >,
        meanStdDevMask_<SqrC3<int, double> >,
        meanStdDevMask_<SqrC3<float, double> >,
        meanStdDevMask_<SqrC3<double, double> >, 0,

        meanStdDevMask_<SqrC4<uchar, double> >, 0,
        meanStdDevMask_<SqrC4<ushort, double> >,
        meanStdDevMask_<SqrC4<short, double> >,
        meanStdDevMask_<SqrC4<int, double> >,
        meanStdDevMask_<SqrC4<float, double> >,
        meanStdDevMask_<SqrC4<double, double> >, 0
    };

    CV_Assert(m.channels() <= 4);

    if (!mask.data) {
        MeanStdDevFunc func = tab[m.type()];
        CV_Assert(func != 0);
        func(m, mean, stddev);
    } else {
        MeanStdDevMaskFunc func = mtab[m.type()];
        CV_Assert(mask.size() == m.size() && mask.type() == CV_8U && func != 0);
        func(m, mask, mean, stddev);
    }
}


/****************************************************************************************\
*                                       minMaxLoc                                        *
\****************************************************************************************/

template<typename T> static void
minMaxIndx_(const Mat& srcmat, double* minVal, double* maxVal, int* minLoc, int* maxLoc) {
    assert(DataType<T>::type == srcmat.type());
    const T* src = (const T*)srcmat.data;
    size_t step = srcmat.step / sizeof(src[0]);
    T min_val = src[0], max_val = min_val;
    int min_loc = 0, max_loc = 0;
    int x, loc = 0;
    Size size = getContinuousSize(srcmat);

    for (; size.height--; src += step, loc += size.width) {
        for (x = 0; x < size.width; x++) {
            T val = src[x];
            if (val < min_val) {
                min_val = val;
                min_loc = loc + x;
            } else if (val > max_val) {
                max_val = val;
                max_loc = loc + x;
            }
        }
    }

    *minLoc = min_loc;
    *maxLoc = max_loc;
    *minVal = min_val;
    *maxVal = max_val;
}


template<typename T> static void
minMaxIndxMask_(const Mat& srcmat, const Mat& maskmat,
                double* minVal, double* maxVal, int* minLoc, int* maxLoc) {
    assert(DataType<T>::type == srcmat.type() &&
           CV_8U == maskmat.type() &&
           srcmat.size() == maskmat.size());
    const T* src = (const T*)srcmat.data;
    const uchar* mask = maskmat.data;
    size_t step = srcmat.step / sizeof(src[0]);
    size_t maskstep = maskmat.step;
    T min_val = 0, max_val = 0;
    int min_loc = -1, max_loc = -1;
    int x = 0, y, loc = 0;
    Size size = getContinuousSize(srcmat, maskmat);

    for (y = 0; y < size.height; y++, src += step, mask += maskstep, loc += size.width) {
        for (x = 0; x < size.width; x++)
            if (mask[x] != 0) {
                min_loc = max_loc = loc + x;
                min_val = max_val = src[x];
                break;
            }
        if (x < size.width) {
            break;
        }
    }

    for (; y < size.height; x = 0, y++, src += step, mask += maskstep, loc += size.width) {
        for (; x < size.width; x++) {
            T val = src[x];
            int m = mask[x];

            if (val < min_val && m) {
                min_val = val;
                min_loc = loc + x;
            } else if (val > max_val && m) {
                max_val = val;
                max_loc = loc + x;
            }
        }
    }

    *minLoc = min_loc;
    *maxLoc = max_loc;
    *minVal = min_val;
    *maxVal = max_val;
}

typedef void (*MinMaxIndxFunc)(const Mat&, double*, double*, int*, int*);

typedef void (*MinMaxIndxMaskFunc)(const Mat&, const Mat&,
                                   double*, double*, int*, int*);

void minMaxLoc(const Mat& img, double* minVal, double* maxVal,
               Point* minLoc, Point* maxLoc, const Mat& mask) {
    static MinMaxIndxFunc tab[] = {
        minMaxIndx_<uchar>, 0, minMaxIndx_<ushort>, minMaxIndx_<short>,
        minMaxIndx_<int>, minMaxIndx_<float>, minMaxIndx_<double>, 0
    };
    static MinMaxIndxMaskFunc tabm[] = {
        minMaxIndxMask_<uchar>, 0, minMaxIndxMask_<ushort>, minMaxIndxMask_<short>,
        minMaxIndxMask_<int>, minMaxIndxMask_<float>, minMaxIndxMask_<double>, 0
    };

    int depth = img.depth();
    double minval = 0, maxval = 0;
    int minloc = 0, maxloc = 0;

    CV_Assert(img.channels() == 1);

    if (!mask.data) {
        MinMaxIndxFunc func = tab[depth];
        CV_Assert(func != 0);
        func(img, &minval, &maxval, &minloc, &maxloc);
    } else {
        CV_Assert(img.size() == mask.size() && mask.type() == CV_8U);
        MinMaxIndxMaskFunc func = tabm[depth];
        CV_Assert(func != 0);
        func(img, mask, &minval, &maxval, &minloc, &maxloc);
    }

    if (minVal) {
        *minVal = minval;
    }
    if (maxVal) {
        *maxVal = maxval;
    }
    if (minLoc) {
        if (minloc >= 0) {
            minLoc->y = minloc / img.cols;
            minLoc->x = minloc - minLoc->y * img.cols;
        } else {
            minLoc->x = minLoc->y = -1;
        }
    }
    if (maxLoc) {
        if (maxloc >= 0) {
            maxLoc->y = maxloc / img.cols;
            maxLoc->x = maxloc - maxLoc->y * img.cols;
        } else {
            maxLoc->x = maxLoc->y = -1;
        }
    }
}

/****************************************************************************************\
*                                         norm                                           *
\****************************************************************************************/

template < typename T, typename WT = T > struct OpAbs {
    typedef T type1;
    typedef WT rtype;
    rtype operator()(type1 x) const { return (WT)std::abs(x); }
};

template<> inline uchar OpAbs<uchar, uchar>::operator()(uchar x) const { return x; }
template<> inline ushort OpAbs<ushort, ushort>::operator()(ushort x) const { return x; }

template<class ElemFunc, class UpdateFunc, class GlobUpdateFunc, int BLOCK_SIZE>
static double normBlock_(const Mat& srcmat) {
    ElemFunc f;
    UpdateFunc update;
    GlobUpdateFunc globUpdate;
    typedef typename ElemFunc::type1 T;
    typedef typename UpdateFunc::rtype WT;
    typedef typename GlobUpdateFunc::rtype ST;

    assert(DataType<T>::depth == srcmat.depth());
    Size size = getContinuousSize(srcmat, srcmat.channels());
    ST s0 = 0; // luckily, 0 is the correct starting value for both + and max update operations
    WT s = 0;
    int y, remaining = BLOCK_SIZE;

    for (y = 0; y < size.height; y++) {
        const T* src = (const T*)(srcmat.data + srcmat.step * y);
        int x = 0;
        while (x < size.width) {
            int limit = std::min(remaining, size.width - x);
            remaining -= limit;
            limit += x;
            for (; x <= limit - 4; x += 4) {
                s = update(s, (WT)f(src[x]));
                s = update(s, (WT)f(src[x + 1]));
                s = update(s, (WT)f(src[x + 2]));
                s = update(s, (WT)f(src[x + 3]));
            }
            for (; x < limit; x++) {
                s = update(s, (WT)f(src[x]));
            }
            if (remaining == 0 || (x == size.width && y == size.height - 1)) {
                s0 = globUpdate(s0, (ST)s);
                s = 0;
                remaining = BLOCK_SIZE;
            }
        }
    }
    return s0;
}

template<class ElemFunc, class UpdateFunc>
static double norm_(const Mat& srcmat) {
    ElemFunc f;
    UpdateFunc update;
    typedef typename ElemFunc::type1 T;
    typedef typename UpdateFunc::rtype ST;

    assert(DataType<T>::depth == srcmat.depth());
    Size size = getContinuousSize(srcmat, srcmat.channels());
    ST s = 0;

    for (int y = 0; y < size.height; y++) {
        const T* src = (const T*)(srcmat.data + srcmat.step * y);
        int x = 0;
        for (; x <= size.width - 4; x += 4) {
            s = update(s, (ST)f(src[x]));
            s = update(s, (ST)f(src[x + 1]));
            s = update(s, (ST)f(src[x + 2]));
            s = update(s, (ST)f(src[x + 3]));
        }
        for (; x < size.width; x++) {
            s = update(s, (ST)f(src[x]));
        }
    }
    return s;
}

template<class ElemFunc, class UpdateFunc, class GlobUpdateFunc, int BLOCK_SIZE>
static double normMaskBlock_(const Mat& srcmat, const Mat& maskmat) {
    ElemFunc f;
    UpdateFunc update;
    GlobUpdateFunc globUpdate;
    typedef typename ElemFunc::type1 T;
    typedef typename UpdateFunc::rtype WT;
    typedef typename GlobUpdateFunc::rtype ST;

    assert(DataType<T>::depth == srcmat.depth());
    Size size = getContinuousSize(srcmat, maskmat);
    ST s0 = 0;
    WT s = 0;
    int y, remaining = BLOCK_SIZE;

    for (y = 0; y < size.height; y++) {
        const T* src = (const T*)(srcmat.data + srcmat.step * y);
        const uchar* mask = maskmat.data + maskmat.step * y;
        int x = 0;
        while (x < size.width) {
            int limit = std::min(remaining, size.width - x);
            remaining -= limit;
            limit += x;
            for (; x <= limit - 4; x += 4) {
                if (mask[x]) {
                    s = update(s, (WT)f(src[x]));
                }
                if (mask[x + 1]) {
                    s = update(s, (WT)f(src[x + 1]));
                }
                if (mask[x + 2]) {
                    s = update(s, (WT)f(src[x + 2]));
                }
                if (mask[x + 3]) {
                    s = update(s, (WT)f(src[x + 3]));
                }
            }
            for (; x < limit; x++) {
                if (mask[x]) {
                    s = update(s, (WT)f(src[x]));
                }
            }
            if (remaining == 0 || (x == size.width && y == size.height - 1)) {
                s0 = globUpdate(s0, (ST)s);
                s = 0;
                remaining = BLOCK_SIZE;
            }
        }
    }
    return s0;
}

template<class ElemFunc, class UpdateFunc>
static double normMask_(const Mat& srcmat, const Mat& maskmat) {
    ElemFunc f;
    UpdateFunc update;
    typedef typename ElemFunc::type1 T;
    typedef typename UpdateFunc::rtype ST;

    assert(DataType<T>::depth == srcmat.depth());
    Size size = getContinuousSize(srcmat, maskmat);
    ST s = 0;

    for (int y = 0; y < size.height; y++) {
        const T* src = (const T*)(srcmat.data + srcmat.step * y);
        const uchar* mask = maskmat.data + maskmat.step * y;
        int x = 0;
        for (; x <= size.width - 4; x += 4) {
            if (mask[x]) {
                s = update(s, (ST)f(src[x]));
            }
            if (mask[x + 1]) {
                s = update(s, (ST)f(src[x + 1]));
            }
            if (mask[x + 2]) {
                s = update(s, (ST)f(src[x + 2]));
            }
            if (mask[x + 3]) {
                s = update(s, (ST)f(src[x + 3]));
            }
        }
        for (; x < size.width; x++) {
            if (mask[x]) {
                s = update(s, (ST)f(src[x]));
            }
        }
    }
    return s;
}

template<typename T, class ElemFunc, class UpdateFunc, class GlobUpdateFunc, int BLOCK_SIZE>
static double normDiffBlock_(const Mat& srcmat1, const Mat& srcmat2) {
    ElemFunc f;
    UpdateFunc update;
    GlobUpdateFunc globUpdate;
    typedef typename UpdateFunc::rtype WT;
    typedef typename GlobUpdateFunc::rtype ST;

    assert(DataType<T>::depth == srcmat1.depth());
    Size size = getContinuousSize(srcmat1, srcmat2, srcmat1.channels());
    ST s0 = 0;
    WT s = 0;
    int y, remaining = BLOCK_SIZE;

    for (y = 0; y < size.height; y++) {
        const T* src1 = (const T*)(srcmat1.data + srcmat1.step * y);
        const T* src2 = (const T*)(srcmat2.data + srcmat2.step * y);
        int x = 0;
        while (x < size.width) {
            int limit = std::min(remaining, size.width - x);
            remaining -= limit;
            limit += x;
            for (; x <= limit - 4; x += 4) {
                s = update(s, (WT)f(src1[x] - src2[x]));
                s = update(s, (WT)f(src1[x + 1] - src2[x + 1]));
                s = update(s, (WT)f(src1[x + 2] - src2[x + 2]));
                s = update(s, (WT)f(src1[x + 3] - src2[x + 3]));
            }
            for (; x < limit; x++) {
                s = update(s, (WT)f(src1[x] - src2[x]));
            }
            if (remaining == 0 || (x == size.width && y == size.height - 1)) {
                s0 = globUpdate(s0, (ST)s);
                s = 0;
                remaining = BLOCK_SIZE;
            }
        }
    }
    return s0;
}

template<typename T, class ElemFunc, class UpdateFunc>
static double normDiff_(const Mat& srcmat1, const Mat& srcmat2) {
    ElemFunc f;
    UpdateFunc update;
    typedef typename UpdateFunc::rtype ST;

    assert(DataType<T>::depth == srcmat1.depth());
    Size size = getContinuousSize(srcmat1, srcmat2, srcmat1.channels());
    ST s = 0;

    for (int y = 0; y < size.height; y++) {
        const T* src1 = (const T*)(srcmat1.data + srcmat1.step * y);
        const T* src2 = (const T*)(srcmat2.data + srcmat2.step * y);
        int x = 0;
        for (; x <= size.width - 4; x += 4) {
            s = update(s, (ST)f(src1[x] - src2[x]));
            s = update(s, (ST)f(src1[x + 1] - src2[x + 1]));
            s = update(s, (ST)f(src1[x + 2] - src2[x + 2]));
            s = update(s, (ST)f(src1[x + 3] - src2[x + 3]));
        }
        for (; x < size.width; x++) {
            s = update(s, (ST)f(src1[x] - src2[x]));
        }
    }
    return s;
}

template<typename T, class ElemFunc, class UpdateFunc, class GlobUpdateFunc, int BLOCK_SIZE>
static double normDiffMaskBlock_(const Mat& srcmat1, const Mat& srcmat2, const Mat& maskmat) {
    ElemFunc f;
    UpdateFunc update;
    GlobUpdateFunc globUpdate;
    typedef typename UpdateFunc::rtype WT;
    typedef typename GlobUpdateFunc::rtype ST;

    assert(DataType<T>::depth == srcmat1.depth());
    Size size = getContinuousSize(srcmat1, srcmat2, maskmat);
    ST s0 = 0;
    WT s = 0;
    int y, remaining = BLOCK_SIZE;

    for (y = 0; y < size.height; y++) {
        const T* src1 = (const T*)(srcmat1.data + srcmat1.step * y);
        const T* src2 = (const T*)(srcmat2.data + srcmat2.step * y);
        const uchar* mask = maskmat.data + maskmat.step * y;
        int x = 0;
        while (x < size.width) {
            int limit = std::min(remaining, size.width - x);
            remaining -= limit;
            limit += x;
            for (; x <= limit - 4; x += 4) {
                if (mask[x]) {
                    s = update(s, (WT)f(src1[x] - src2[x]));
                }
                if (mask[x + 1]) {
                    s = update(s, (WT)f(src1[x + 1] - src2[x + 1]));
                }
                if (mask[x + 2]) {
                    s = update(s, (WT)f(src1[x + 2] - src2[x + 2]));
                }
                if (mask[x + 3]) {
                    s = update(s, (WT)f(src1[x + 3] - src2[x + 3]));
                }
            }
            for (; x < limit; x++)
                if (mask[x]) {
                    s = update(s, (WT)f(src1[x] - src2[x]));
                }
            if (remaining == 0 || (x == size.width && y == size.height - 1)) {
                s0 = globUpdate(s0, (ST)s);
                s = 0;
                remaining = BLOCK_SIZE;
            }
        }
    }
    return s0;
}

template<typename T, class ElemFunc, class UpdateFunc>
static double normDiffMask_(const Mat& srcmat1, const Mat& srcmat2, const Mat& maskmat) {
    ElemFunc f;
    UpdateFunc update;
    typedef typename UpdateFunc::rtype ST;

    assert(DataType<T>::depth == srcmat1.depth());
    Size size = getContinuousSize(srcmat1, srcmat2, maskmat);
    ST s = 0;

    for (int y = 0; y < size.height; y++) {
        const T* src1 = (const T*)(srcmat1.data + srcmat1.step * y);
        const T* src2 = (const T*)(srcmat2.data + srcmat2.step * y);
        const uchar* mask = maskmat.data + maskmat.step * y;
        int x = 0;
        for (; x <= size.width - 4; x += 4) {
            if (mask[x]) {
                s = update(s, (ST)f(src1[x] - src2[x]));
            }
            if (mask[x + 1]) {
                s = update(s, (ST)f(src1[x + 1] - src2[x + 1]));
            }
            if (mask[x + 2]) {
                s = update(s, (ST)f(src1[x + 2] - src2[x + 2]));
            }
            if (mask[x + 3]) {
                s = update(s, (ST)f(src1[x + 3] - src2[x + 3]));
            }
        }
        for (; x < size.width; x++)
            if (mask[x]) {
                s = update(s, (ST)f(src1[x] - src2[x]));
            }

    }
    return s;
}

typedef double(*NormFunc)(const Mat& src);
typedef double(*NormDiffFunc)(const Mat& src1, const Mat& src2);
typedef double(*NormMaskFunc)(const Mat& src1, const Mat& mask);
typedef double(*NormDiffMaskFunc)(const Mat& src1, const Mat& src2, const Mat& mask);

double norm(const Mat& a, int normType) {
    static NormFunc tab[3][8] = {
        {
            norm_<OpAbs<uchar>, OpMax<int> >,
            norm_<OpAbs<schar>, OpMax<int> >,
            norm_<OpAbs<ushort>, OpMax<int> >,
            norm_<OpAbs<short, int>, OpMax<int> >,
            norm_<OpAbs<int>, OpMax<int> >,
            norm_<OpAbs<float>, OpMax<float> >,
            norm_<OpAbs<double>, OpMax<double> >
        },

        {
            normBlock_ < OpAbs<uchar>, OpAdd<unsigned>, OpAdd<double>, 1 << 24 > ,
            normBlock_ < OpAbs<schar>, OpAdd<unsigned>, OpAdd<double>, 1 << 24 > ,
            normBlock_ < OpAbs<ushort>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            normBlock_ < OpAbs<short, int>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            norm_<OpAbs<int>, OpAdd<double> >,
            norm_<OpAbs<float>, OpAdd<double> >,
            norm_<OpAbs<double>, OpAdd<double> >
        },

        {
            normBlock_ < SqrC1<uchar, unsigned>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            normBlock_ < SqrC1<schar, unsigned>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            norm_<SqrC1<ushort, double>, OpAdd<double> >,
            norm_<SqrC1<short, double>, OpAdd<double> >,
            norm_<SqrC1<int, double>, OpAdd<double> >,
            norm_<SqrC1<float, double>, OpAdd<double> >,
            norm_<SqrC1<double, double>, OpAdd<double> >
        }
    };

    normType &= 7;
    CV_Assert(normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2);
    NormFunc func = tab[normType >> 1][a.depth()];
    CV_Assert(func != 0);
    double r = func(a);
    return normType == NORM_L2 ? std::sqrt(r) : r;
}


double norm(const Mat& a, int normType, const Mat& mask) {
    static NormMaskFunc tab[3][8] = {
        {
            normMask_<OpAbs<uchar>, OpMax<int> >,
            normMask_<OpAbs<schar>, OpMax<int> >,
            normMask_<OpAbs<ushort>, OpMax<int> >,
            normMask_<OpAbs<short, int>, OpMax<int> >,
            normMask_<OpAbs<int>, OpMax<int> >,
            normMask_<OpAbs<float>, OpMax<float> >,
            normMask_<OpAbs<double>, OpMax<double> >
        },

        {
            normMaskBlock_ < OpAbs<uchar>, OpAdd<unsigned>, OpAdd<double>, 1 << 24 > ,
            normMaskBlock_ < OpAbs<schar>, OpAdd<unsigned>, OpAdd<double>, 1 << 24 > ,
            normMaskBlock_ < OpAbs<ushort>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            normMaskBlock_ < OpAbs<short, int>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            normMask_<OpAbs<int>, OpAdd<double> >,
            normMask_<OpAbs<float>, OpAdd<double> >,
            normMask_<OpAbs<double>, OpAdd<double> >
        },

        {
            normMaskBlock_ < SqrC1<uchar, unsigned>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            normMaskBlock_ < SqrC1<schar, unsigned>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            normMask_<SqrC1<ushort, double>, OpAdd<double> >,
            normMask_<SqrC1<short, double>, OpAdd<double> >,
            normMask_<SqrC1<int, double>, OpAdd<double> >,
            normMask_<SqrC1<float, double>, OpAdd<double> >,
            normMask_<SqrC1<double, double>, OpAdd<double> >
        }
    };

    if (!mask.data) {
        return norm(a, normType);
    }

    normType &= 7;
    CV_Assert((normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2) &&
              a.size() == mask.size() && mask.type() == CV_8U && a.channels() == 1);
    NormMaskFunc func = tab[normType >> 1][a.depth()];
    CV_Assert(func != 0);
    double r = func(a, mask);
    return normType == NORM_L2 ? std::sqrt(r) : r;
}


double norm(const Mat& a, const Mat& b, int normType) {
    static NormDiffFunc tab[3][8] = {
        {
            normDiff_<uchar, OpAbs<int>, OpMax<int> >,
            normDiff_<schar, OpAbs<int>, OpMax<int> >,
            normDiff_<ushort, OpAbs<int>, OpMax<int> >,
            normDiff_<short, OpAbs<int>, OpMax<int> >,
            normDiff_<int, OpAbs<int>, OpMax<int> >,
            normDiff_<float, OpAbs<float>, OpMax<float> >,
            normDiff_<double, OpAbs<double>, OpMax<double> >
        },

        {
            normDiffBlock_ < uchar, OpAbs<int>, OpAdd<unsigned>, OpAdd<double>, 1 << 24 > ,
            normDiffBlock_ < schar, OpAbs<int>, OpAdd<unsigned>, OpAdd<double>, 1 << 24 > ,
            normDiffBlock_ < ushort, OpAbs<int>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            normDiffBlock_ < short, OpAbs<int>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            normDiff_<int, OpAbs<int>, OpAdd<double> >,
            normDiff_<float, OpAbs<float>, OpAdd<double> >,
            normDiff_<double, OpAbs<double>, OpAdd<double> >
        },

        {
            normDiffBlock_ < uchar, SqrC1<int, int>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            normDiffBlock_ < schar, SqrC1<int, int>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            normDiff_<ushort, SqrC1<int, double>, OpAdd<double> >,
            normDiff_<short, SqrC1<int, double>, OpAdd<double> >,
            normDiff_<int, SqrC1<int, double>, OpAdd<double> >,
            normDiff_<float, SqrC1<float, double>, OpAdd<double> >,
            normDiff_<double, SqrC1<double, double>, OpAdd<double> >
        }
    };

    CV_Assert(a.type() == b.type() && a.size() == b.size());

    bool isRelative = (normType & NORM_RELATIVE) != 0;
    normType &= 7;
    CV_Assert(normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2);

    NormDiffFunc func = tab[normType >> 1][a.depth()];
    CV_Assert(func != 0);
    double r = func(a, b);
    if (normType == NORM_L2) {
        r = std::sqrt(r);
    }
    if (isRelative) {
        r /= norm(b, normType);
    }
    return r;
}

double norm(const Mat& a, const Mat& b, int normType, const Mat& mask) {
    static NormDiffMaskFunc tab[3][8] = {
        {
            normDiffMask_<uchar, OpAbs<int>, OpMax<int> >,
            normDiffMask_<schar, OpAbs<int>, OpMax<int> >,
            normDiffMask_<ushort, OpAbs<int>, OpMax<int> >,
            normDiffMask_<short, OpAbs<int>, OpMax<int> >,
            normDiffMask_<int, OpAbs<int>, OpMax<int> >,
            normDiffMask_<float, OpAbs<float>, OpMax<float> >,
            normDiffMask_<double, OpAbs<double>, OpMax<double> >
        },

        {
            normDiffMaskBlock_ < uchar, OpAbs<int>, OpAdd<unsigned>, OpAdd<double>, 1 << 24 > ,
            normDiffMaskBlock_ < schar, OpAbs<int>, OpAdd<unsigned>, OpAdd<double>, 1 << 24 > ,
            normDiffMaskBlock_ < ushort, OpAbs<int>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            normDiffMaskBlock_ < short, OpAbs<int>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            normDiffMask_<int, OpAbs<int>, OpAdd<double> >,
            normDiffMask_<float, OpAbs<float>, OpAdd<double> >,
            normDiffMask_<double, OpAbs<double>, OpAdd<double> >
        },

        {
            normDiffMaskBlock_ < uchar, SqrC1<int, int>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            normDiffMaskBlock_ < schar, SqrC1<int, int>, OpAdd<unsigned>, OpAdd<double>, 1 << 16 > ,
            normDiffMask_<ushort, SqrC1<int, double>, OpAdd<double> >,
            normDiffMask_<short, SqrC1<int, double>, OpAdd<double> >,
            normDiffMask_<int, SqrC1<int, double>, OpAdd<double> >,
            normDiffMask_<float, SqrC1<float, double>, OpAdd<double> >,
            normDiffMask_<double, SqrC1<double, double>, OpAdd<double> >
        }
    };

    if (!mask.data) {
        return norm(a, b, normType);
    }

    CV_Assert(a.type() == b.type() && a.size() == b.size() &&
              a.size() == mask.size() && mask.type() == CV_8U && a.channels() == 1);

    bool isRelative = (normType & NORM_RELATIVE) != 0;
    normType &= 7;
    CV_Assert(normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2);

    NormDiffMaskFunc func = tab[normType >> 1][a.depth()];
    CV_Assert(func != 0);
    double r = func(a, b, mask);
    if (normType == NORM_L2) {
        r = std::sqrt(r);
    }
    if (isRelative) {
        r /= std::max(norm(b, normType, mask), DBL_EPSILON);
    }
    return r;
}

}


CV_IMPL CvScalar cvSum(const CvArr* srcarr) {
    cv::Scalar sum = cv::sum(cv::cvarrToMat(srcarr, false, true, 1));
    if (CV_IS_IMAGE(srcarr)) {
        int coi = cvGetImageCOI((IplImage*)srcarr);
        if (coi) {
            CV_Assert(0 < coi && coi <= 4);
            sum = cv::Scalar(sum[coi - 1]);
        }
    }
    return sum;
}

CV_IMPL int cvCountNonZero(const CvArr* imgarr) {
    cv::Mat img = cv::cvarrToMat(imgarr, false, true, 1);
    if (img.channels() > 1) {
        cv::extractImageCOI(imgarr, img);
    }
    return countNonZero(img);
}


CV_IMPL  CvScalar
cvAvg(const void* imgarr, const void* maskarr) {
    cv::Mat img = cv::cvarrToMat(imgarr, false, true, 1);
    cv::Scalar mean = !maskarr ? cv::mean(img) : cv::mean(img, cv::cvarrToMat(maskarr));
    if (CV_IS_IMAGE(imgarr)) {
        int coi = cvGetImageCOI((IplImage*)imgarr);
        if (coi) {
            CV_Assert(0 < coi && coi <= 4);
            mean = cv::Scalar(mean[coi - 1]);
        }
    }
    return mean;
}


CV_IMPL  void
cvAvgSdv(const CvArr* imgarr, CvScalar* _mean, CvScalar* _sdv, const void* maskarr) {
    cv::Scalar mean, sdv;

    cv::Mat mask;
    if (maskarr) {
        mask = cv::cvarrToMat(maskarr);
    }

    cv::meanStdDev(cv::cvarrToMat(imgarr, false, true, 1), mean, sdv, mask);

    if (CV_IS_IMAGE(imgarr)) {
        int coi = cvGetImageCOI((IplImage*)imgarr);
        if (coi) {
            CV_Assert(0 < coi && coi <= 4);
            mean = cv::Scalar(mean[coi - 1]);
            sdv = cv::Scalar(sdv[coi - 1]);
        }
    }

    if (_mean) {
        *(cv::Scalar*)_mean = mean;
    }
    if (_sdv) {
        *(cv::Scalar*)_sdv = sdv;
    }
}


CV_IMPL void
cvMinMaxLoc(const void* imgarr, double* _minVal, double* _maxVal,
            CvPoint* _minLoc, CvPoint* _maxLoc, const void* maskarr) {
    cv::Mat mask, img = cv::cvarrToMat(imgarr, false, true, 1);
    if (maskarr) {
        mask = cv::cvarrToMat(maskarr);
    }
    if (img.channels() > 1) {
        cv::extractImageCOI(imgarr, img);
    }

    cv::minMaxLoc(img, _minVal, _maxVal,
                  (cv::Point*)_minLoc, (cv::Point*)_maxLoc, mask);
}


CV_IMPL  double
cvNorm(const void* imgA, const void* imgB, int normType, const void* maskarr) {
    cv::Mat a, mask;
    if (!imgA) {
        imgA = imgB;
        imgB = 0;
    }

    a = cv::cvarrToMat(imgA, false, true, 1);
    if (maskarr) {
        mask = cv::cvarrToMat(maskarr);
    }

    if (a.channels() > 1 && CV_IS_IMAGE(imgA) && cvGetImageCOI((const IplImage*)imgA) > 0) {
        cv::extractImageCOI(imgA, a);
    }

    if (!imgB) {
        return !maskarr ? cv::norm(a, normType) : cv::norm(a, normType, mask);
    }

    cv::Mat b = cv::cvarrToMat(imgB, false, true, 1);
    if (b.channels() > 1 && CV_IS_IMAGE(imgB) && cvGetImageCOI((const IplImage*)imgB) > 0) {
        cv::extractImageCOI(imgB, b);
    }

    return !maskarr ? cv::norm(a, b, normType) : cv::norm(a, b, normType, mask);
}
